﻿using Common.EF.Structs;
using Common.Infrastructure;
using Common.Infrastructure.Service;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;

namespace Common.EF
{
    public class CommonRepository : ICommonRepository, IDisposable
    {
        private readonly ICommonContextPool contextPool;
        private readonly IServiceProvider provider;
        private readonly IConfiguration configuration;
        private readonly IComonCacheService comonCacheService;

        public CommonRepository(IServiceProvider provider, IConfiguration configuration, ICommonContextPool pool, IComonCacheService comonCache)
        {
            this.contextPool = pool;
            this.provider = provider;
            this.configuration = configuration;
            this.comonCacheService = comonCache;
        }

        public ICommonContextPool ContextPool
        {
            get { return contextPool; }
        } 

        public IServiceProvider Provider
        {
            get { return provider; }
        }

        public async Task<T> GetEntityAsync<T>(string id, params string[] includes)
            where T : EntityBase, new()
        {
            LibCheckNullUtils.CheckNull(id, "Id");
            return await this.ContextPool.GetEntityAsync<T>(id, includes);
        }

        public async Task<T> GetEntityAsync<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate, string[] includes = null) 
            where T : EntityBase, new()
        {
            var temOrders = this.GetOrders(apiParam, null);
            var tempIncludes = this.GetIncludes(apiParam, includes);
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return await this.ContextPool.GetEntityAsync<T>(beginTime, endTime, predicate, temOrders, contextCodes, tempIncludes);
        }

        public async Task<List<T>> GetEntitiesAsync<T>(List<string> ids, string[] includes = null) 
            where T : EntityBase, new()
        {
            return await GetEntitiesAsync<T>(null, ids, includes);
        }

        public async Task<List<T>> GetEntitiesAsync<T>(ApiParam apiParam, List<string> ids, string[] includes = null)
            where T : EntityBase, new()
        {
            var tempIncludes = this.GetIncludes(apiParam, includes);
            return await this.ContextPool.GetEntitiesAsync<T>(ids, tempIncludes);
        }

        public async Task<List<T>> GetEntitiesAsync<T>(Expression<Func<T, bool>> predicate, string[] includes = null)
            where T : EntityBase, new()
        {
            return await GetEntitiesAsync(null, predicate, includes);
        }

        public async Task<List<T>> GetEntitiesAsync<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate,  string[] includes = null)
            where T : EntityBase, new()
        {
            var tempIncludes = this.GetIncludes(apiParam, includes);
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes); 
            return await this.ContextPool.GetEntitiesAsync<T>(beginTime, endTime, predicate, contextCodes, tempIncludes);
        }

        public async Task<List<T>> GetOrderEntitiesAsync<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate, string[] includes = null)
            where T : EntityBase, new()
        {
            return await GetOrderEntitiesAsync(apiParam, predicate, null, includes);
        }

        public async Task<List<T>> GetOrderEntitiesAsync<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate, Dictionary<string, int> orders, string[] includes = null)
            where T : EntityBase, new()
        {
            var temOrders = this.GetOrders(apiParam, orders);
            var tempIncludes = this.GetIncludes(apiParam, includes); 
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return await this.ContextPool.GetOrderEntitiesAsync<T>(beginTime, endTime, predicate, temOrders, contextCodes, tempIncludes);
        }

        public async Task<ApiPagedResult<T>> GetOrderPageEntitiesAsync<T>(ApiOrderPageParam apiOrderPageParam, Expression<Func<T, bool>> predicate, string[] includes = null)
           where T : EntityBase, new()
        {
            return await GetOrderPageEntitiesAsync<T>(apiOrderPageParam, predicate, null, 0, 0, includes);
        }

        public async Task<ApiPagedResult<T>> GetOrderPageEntitiesAsync<T>(Expression<Func<T, bool>> predicate, Dictionary<string, int> orders, int startIndex, int pageSize, string[] includes = null)
            where T : EntityBase, new()
        {
            return await GetOrderPageEntitiesAsync<T>(null, predicate, orders, startIndex, pageSize, includes);
        }

        public async Task<ApiPagedResult<T>> GetOrderPageEntitiesAsync<T>(ApiOrderPageParam apiOrderPageParam, Expression<Func<T, bool>> predicate, Dictionary<string, int> orders, int startIndex, int pageSize, string[] includes = null)
            where T : EntityBase, new()
        {
            var temOrders = this.GetOrders(apiOrderPageParam, orders);
            var tempIncludes = this.GetIncludes(apiOrderPageParam, includes); 
            int tempStartIndex = startIndex;
            int tempPageSize = pageSize;
            if (apiOrderPageParam != null && apiOrderPageParam.PageSize != 0)
            {
                tempStartIndex = apiOrderPageParam.StartIndex;
                tempPageSize = apiOrderPageParam.PageSize;
            } 

            GetBeginTimeEndTimeConntextExp(apiOrderPageParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return await this.ContextPool.GetOrderPageEntitiesAsync<T>(beginTime, endTime, predicate, temOrders, tempStartIndex, tempPageSize, contextCodes, tempIncludes);
        }

        public async Task<int> GetCountAsync<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate)
          where T : EntityBase, new()
        {
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return await this.ContextPool.GetCountAsync<T>(beginTime, endTime, predicate, contextCodes);
        }

        public async Task<decimal> GetSumAsync<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate, Expression<Func<T, decimal>> sumExpression) where T : EntityBase, new()
        {
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return await this.ContextPool.GetSumAsync<T>(beginTime, endTime, predicate, sumExpression, contextCodes);
        }

        public async Task<List<IGrouping<S, T>>> GetGroupByAsync<T, S>(ApiParam apiParam, Expression<Func<T, bool>> predicate, Expression<Func<T, S>> groupByExpression)
            where T : EntityBase, new()
        {
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return await this.ContextPool.GetGroupByAsync<T, S>(beginTime, endTime, predicate, groupByExpression, contextCodes);
        }

        public T GetEntity<T>(string id, params string[] includes) 
            where T : EntityBase, new()
        {
            return this.ContextPool.GetEntity<T>(id, includes);
        }

        public T GetEntity<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate, string[] includes = null) where T : EntityBase, new()
        {
            var temOrders = this.GetOrders(apiParam, null);
            var tempIncludes = this.GetIncludes(apiParam, includes);
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return this.ContextPool.GetEntity<T>(beginTime, endTime, predicate, temOrders, contextCodes, tempIncludes);
        }

        public List<T> GetEntities<T>(List<string> ids, string[] includes = null) where T : EntityBase, new()
        {
            return this.ContextPool.GetEntities<T>(ids, includes);
        }
         
        public List<T> GetEntities<T>(Expression<Func<T, bool>> predicate, string[] includes = null) where T : EntityBase, new()
        {
            return GetEntities(null, predicate, includes);
        }

        public List<T> GetEntities<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate = null, string[] includes = null) where T : EntityBase, new()
        {
            var tempIncludes = this.GetIncludes(apiParam, includes);
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return this.ContextPool.GetEntities<T>(beginTime, endTime, predicate, contextCodes, tempIncludes);
        }

        public List<T> GetOrderEntities<T>(Expression<Func<T, bool>> predicate, string[] includes = null)
           where T : EntityBase, new()
        {
            return GetOrderEntities(null, predicate, null, includes);
        }

        public List<T> GetOrderEntities<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate = null, string[] includes = null)
            where T : EntityBase, new()
        {
            return GetOrderEntities(apiParam, predicate, null, includes);
        }

        public List<T> GetOrderEntities<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate, Dictionary<string, int> orders, string[] includes = null)
            where T : EntityBase, new()
        {
            var temOrders = this.GetOrders(apiParam, orders);
            var tempIncludes = this.GetIncludes(apiParam, includes);
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return this.ContextPool.GetOrderEntities<T>(beginTime, endTime, predicate, temOrders, contextCodes, tempIncludes);
        }

        public ApiPagedResult<T> GetOrderPageEntities<T>(ApiOrderPageParam apiOrderPageParam, Expression<Func<T, bool>> predicate = null, string[] includes = null)
           where T : EntityBase, new()
        {
            return GetOrderPageEntities<T>(apiOrderPageParam, predicate, null, 0, 0, includes);
        }

        public ApiPagedResult<T> GetOrderPageEntities<T>(Expression<Func<T, bool>> predicate, Dictionary<string, int> orders, int startIndex, int pageSize, string[] includes = null)
            where T : EntityBase, new()
        {
            return GetOrderPageEntities<T>(null, predicate, orders, startIndex, pageSize, includes);
        }

        public ApiPagedResult<T> GetOrderPageEntities<T>(ApiOrderPageParam apiOrderPageParam, Expression<Func<T, bool>> predicate, Dictionary<string, int> orders, int startIndex, int pageSize, string[] includes = null)
            where T : EntityBase, new()
        {
            var temOrders = this.GetOrders(apiOrderPageParam, orders);
            var tempIncludes = this.GetIncludes(apiOrderPageParam, includes); 
            int tempStartIndex = startIndex;
            int tempPageSize = pageSize;
            if (apiOrderPageParam != null && apiOrderPageParam.PageSize != 0)
            {
                tempStartIndex = apiOrderPageParam.StartIndex;
                tempPageSize = apiOrderPageParam.PageSize;
            }

            GetBeginTimeEndTimeConntextExp(apiOrderPageParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return this.ContextPool.GetOrderPageEntities<T>(beginTime, endTime, predicate, temOrders, tempStartIndex, tempPageSize, contextCodes, tempIncludes);
        }

        public int GetCount<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate)
          where T : EntityBase, new()
        {
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return this.ContextPool.GetCount<T>(beginTime, endTime, predicate, contextCodes);
        }

        public decimal GetSum<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate, Expression<Func<T, decimal>> sumExpression) where T : EntityBase, new()
        {
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return this.ContextPool.GetSum<T>(beginTime, endTime, predicate, sumExpression, contextCodes); 
        }

        public List<IGrouping<S, T>> GetGroupBy<T, S>(ApiParam apiParam, Expression<Func<T, bool>> predicate, Expression<Func<T, S>> groupByExpression)
          where T : EntityBase, new()
        {
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            return this.ContextPool.GetGroupBy<T, S>(beginTime, endTime, predicate, groupByExpression, contextCodes);
        }

        public void AddEntity<T>(T entity)
            where T : EntityBase, new()
        {
            if (entity == null)
            {
                return;
            }

            List<string> distributedCacheKeys = new List<string>();
            try
            {
                List<T> entities = new List<T>
                {
                    entity
                };
                DoCheckEntityVali(entity);
                CheckUniqueEntities<T>(entities, null, distributedCacheKeys); 
                this.SetEnitityInfo(entity, true); 
                this.ContextPool.AddEntities<T>(entities); 
            }
            finally
            {
                AddRemoveDistributeCacheKeys(distributedCacheKeys);
            }
        }

        public void AddEntities<T>(List<T> entities)
            where T : EntityBase, new()
        {
            if (entities == null || !entities.Any())
            {
                return;
            }

            List<string> distributedCacheKeys = new List<string>();
            try
            {
                CheckEntityVali(entities);
                CheckUniqueEntities<T>(entities, null, distributedCacheKeys); 
                this.SetEnitityInfo(entities.ToList(), true); 
                this.ContextPool.AddEntities<T>(entities); 
            }
            finally
            {
                AddRemoveDistributeCacheKeys(distributedCacheKeys);
            }
        }

        public void UpdateEntity<T>(T entity, params string[] updateProperies)
            where T : EntityBase, new()
        {
            if (entity == null)
            {
                return;
            }

            List<string> distributedCacheKeys = new List<string>();
            try
            {
                DoCheckEntityVali(entity);
                List<T> entities = new List<T>
                {
                    entity
                };
                CheckUniqueEntities<T>(entities, null, distributedCacheKeys);
                if (entity.is_deleted)
                {
                    List<string> ids = new List<string>
                    {
                        entity.id
                    };
                    this.ContextPool.DeleteEntity<T>(ids);
                }
                else
                {
                    var entityDb = this.ContextPool.GetEntity<T>(entity.id, null);
                    List<T> entitiesDb = new List<T>();
                    if (entityDb != null)
                    {
                        entitiesDb.Add(entityDb);
                    }

                    this.SetEnitityInfo(entity, false); 
                    this.ContextPool.UpdateEntities<T>(entities, entitiesDb, updateProperies); 
                } 
            }
            finally
            {
                AddRemoveDistributeCacheKeys(distributedCacheKeys);
            }
        }

        public void UpdateEntities<T>(List<T> entities, params string[] updateProperies)
            where T : EntityBase, new()
        {
            if (entities == null || !entities.Any())
            {
                return;
            }

            List<string> distributedCacheKeys = new List<string>();
            try
            {
                CheckEntityVali(entities); 
                var deleteEntitys = entities.Where(t => t.is_deleted).ToList();
                var updateEntities = entities.Where(t => !t.is_deleted).ToList();
                CheckUniqueEntities<T>(updateEntities, null, distributedCacheKeys);
                if (updateEntities.Any())
                {
                    var updateEntityIds = updateEntities.Select(t => t.id).ToList();
                    var entitiesDB = this.ContextPool.GetEntities<T>(updateEntityIds, null); 
                    this.SetEnitityInfo(updateEntities, false); 
                    this.ContextPool.UpdateEntities<T>(updateEntities, entitiesDB, updateProperies);
                }

                if (deleteEntitys.Any())
                { 
                    var deleteEntityIds = deleteEntitys.Select(t => t.id).ToList(); 
                    this.ContextPool.DeleteEntity<T>(deleteEntityIds);
                } 
            }
            finally
            {
                AddRemoveDistributeCacheKeys(distributedCacheKeys);
            }
        }

        public void SaveEntity<T>(T entity, params string[] updateProperies)
            where T : EntityBase, new()
        {
            if (entity == null)
            {
                return;
            }

            DoCheckEntityVali(entity);
            List<T> entities = new List<T>
            {
                entity
            };
            DoSaveEntity(entities, updateProperies);
        }

        public void SaveEntities<T>(List<T> entities, params string[] updateProperies)
            where T : EntityBase, new()
        {
            DoSaveEntity(entities, updateProperies);
        }

        public void DeleteEntity<T>(string id)
            where T : EntityBase, new()
        {
            if (string.IsNullOrEmpty(id))
            {
                return;
            }

            List<string> list = new List<string>
            {
                id
            };
            this.DeleteEntity<T>(list);
        }

        public void DeleteEntity<T>(List<string> ids)
            where T : EntityBase, new()
        {
            if (ids == null || !ids.Any())
            {
                return;
            }

            this.ContextPool.DeleteEntity<T>(ids); 
        }

        public void DeleteEntity<T>(Expression<Func<T, bool>> predicate)
            where T : EntityBase, new()
        {
            DateTime b = DateTime.Now.Date.AddDays(-30);
            DateTime e = DateTime.Now.Date.AddDays(1).AddSeconds(-1);
            this.ContextPool.DeleteEntity<T>(b, e, predicate, null);
        }

        public void DeleteEntityByCommit<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate)
          where T : EntityBase, new()
        {
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            this.ContextPool.DeleteEntityByCommit<T>(beginTime, endTime, predicate, contextCodes);
        }

        public void UpdateEntitiesByByCommit<T>(ApiParam apiParam, Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updateExpression)
          where T : EntityBase, new()
        {
            GetBeginTimeEndTimeConntextExp(apiParam, ref predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            this.ContextPool.UpdateEntitiesByByCommit<T>(beginTime, endTime, predicate, updateExpression, contextCodes);
        }

        public void ExecuteReaderByContext(ApiParam apiParam, CommandType commandType, string sql, ApiDbParameter[] parameters, Action<DbDataReader> readerAction)
        {
            GetBeginTimeEndTimeConntext(apiParam, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            this.ContextPool.ExecuteReaderByContext(beginTime, endTime, commandType, sql, parameters, readerAction, contextCodes);
        }

        public void ExecuteNonQueryByContext(ApiParam apiParam, CommandType commandType, string sql, ApiDbParameter[] parameters)
        {
            GetBeginTimeEndTimeConntext(apiParam, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes);
            this.ContextPool.ExecuteNonQueryByContext(beginTime, endTime, contextCodes, commandType, sql, parameters);
        }

        public async Task DatabaseMigrateAsync(DateTime? beginTime, DateTime? endTime)
        {
            await this.ContextPool.DatabaseMigrateAsync(beginTime, endTime);
        }

        public List<string> GetEntityIds(IDParam model)
        { 
            return this.ContextPool.GetEntityIds(model);
        }

        public async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            return await this.ContextPool.SaveChangesAsync(cancellationToken);
        }

        public int SaveChanges()
        {
            return this.ContextPool.SaveChanges();
        }

        public string GetContextCodeById(string id)
        {
            return this.ContextPool.GetContextCodeById(id);
        }

        public void RemoveDistributeCacheKeys()
        {
        }

        public async Task CommitChangesAsync(Action func)
        {
            try
            {
                func();
                await DoCommitChangesAsync();
            }
            catch
            {
                throw;
            }
            finally
            {
                this.DoAfterByCommit();
            }
        }

        public async Task CommitChangesByFuncAsync(Func<Task> func)
        {
            try
            {
                await func();
                await DoCommitChangesAsync();
            }
            catch
            {
                throw;
            }
            finally
            {
                this.DoAfterByCommit();
            }
        }

        public void CommitChanges(Action func)
        {
            try
            {
                func();
                this.SaveChanges();
            }
            catch
            {
                throw;
            }
            finally
            {
                this.DoAfterByCommit();
            }
        }

        public ApiParam GetApiParamById(string id)
        {
            if (string.IsNullOrEmpty(id))
            {
                return null;
            }

            var s = new List<string>
            {
                this.GetContextCodeById(id)
            };
            return new ApiParam() { ContextCodes = s };
        }

        public ApiParam GetApiParamByIds(List<string> ids)
        {
            if (ids == null || !ids.Any())
            {
                return null;
            }

            var s = new List<string>();
            ids.ForEach(t => s.Add(this.GetContextCodeById(t)));
            s = s.Distinct().ToList();
            return new ApiParam() { ContextCodes = s };
        }

        public ApiParam GetApiParamByEntity<T>(T entity) where T : EntityBase, new()
        {
            return entity == null ? null : GetApiParamById(entity.id);
        }

        public ApiParam GetApiParamByEntities<T>(List<T> entitis) where T : EntityBase, new()
        {
            return entitis == null || !entitis.Any() ? null : GetApiParamByIds(entitis.Select(t => t.id).ToList());
        }

        public virtual void Dispose()
        {
            if (this.ContextPool != null)
            {
                this.ContextPool.Dispose();
            }
        }

        private async Task DoCommitChangesAsync()
        {
            await this.SaveChangesAsync();
        }

        private void DoAfterByCommit()
        {
            this.RemoveDistributeCacheKeys();
        }

        private void GetBeginEndTime(ApiParam apiParam, ref DateTime? beginTime, ref DateTime? endTime)
        {
            if (apiParam == null || (apiParam.BeginTime == null && apiParam.EndTime == null))
            {
                beginTime = DateTime.Now.Date.AddDays(-30);
                endTime = DateTime.Now.Date.AddDays(1).AddSeconds(-1);
            }
            else
            {
                beginTime = apiParam.BeginTime;
                endTime = apiParam.EndTime;
            }
        }

        private string[] GetIncludes(ApiParam apiParam, string[] includes)
        {
            List<string> tempIncludes = new List<string>();
            if (apiParam != null && apiParam.Includes != null && apiParam.Includes.Any())
            {
                apiParam.Includes.ForEach(t =>
                {
                    if (!tempIncludes.Contains(t))
                    {
                        tempIncludes.Add(t);
                    }
                });
            }

            if (includes != null && includes.Any())
            {
                includes.ToList().ForEach(t =>
                {
                    if (!tempIncludes.Contains(t))
                    {
                        tempIncludes.Add(t);
                    }
                });
            }

            if (tempIncludes.Any())
            {
                return tempIncludes.ToArray();
            }

            return null;
        }

        private Dictionary<string, int> GetOrders(ApiParam apiParam, Dictionary<string, int> orders)
        {
            Dictionary<string, int> tempIncludes = new Dictionary<string, int>();
            if (apiParam != null && !string.IsNullOrEmpty(apiParam.Sort))
            {
                tempIncludes[apiParam.Sort] = (int)apiParam.Order;
            }

            if (apiParam != null && apiParam.Orders != null && apiParam.Orders.Any())
            {
                apiParam.Orders.ForEach(t =>
                {
                    tempIncludes[t.Sort] = (int)t.Order;
                });
            }

            if (orders != null && orders.Any())
            {
                foreach (var item in orders)
                {
                    tempIncludes[item.Key] = item.Value;
                }
            }

            if (tempIncludes.Any())
            {
                return tempIncludes;
            }

            return null;
        }

        private Expression<Func<T, bool>> GetOrgAuthExpression<T>(ApiParam apiParam) where T : EntityBase, new()
        {
            if (apiParam == null || string.IsNullOrEmpty(apiParam.OrgId))
            {
                return null;
            }

            Expression<Func<T, bool>> expression = null;
            if (apiParam.OrgType == FilterOrgTypeEnum.Default)
            {
                var orgTreeNode = this.comonCacheService.GetOrgTreeNodeById(apiParam.OrgId);
                List<string> childrenOrgIds = new List<string>();
                childrenOrgIds.Add(apiParam.OrgId);

                if (apiParam.AliasOrgIds != null && apiParam.AliasOrgIds.Any())
                {
                    childrenOrgIds.AddRange(apiParam.AliasOrgIds);
                }

                if (childrenOrgIds.Count == 1)
                {
                    var orgid = childrenOrgIds[0];
                    expression = t => t.org_id == orgid;
                }
                else
                {
                    expression = t => childrenOrgIds.Contains(t.org_id);
                }
            }
            else if (apiParam.OrgType == FilterOrgTypeEnum.OnlyOrg)
            {
                expression = t => t.org_id == apiParam.OrgId;
            }

            return expression;
        }

        private void GetBeginTimeEndTimeConntextExp<T>(ApiParam apiParam, ref Expression<Func<T, bool>> predicate, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes) where T : EntityBase, new()
        {
            if (predicate == null)
            {
                predicate = t => true;
            }

            if (apiParam != null && apiParam.Group != null)
            {
                predicate = predicate.And(apiParam.Group.GetExpression<T>());
            }

            var tempPredicate = GetOrgAuthExpression<T>(apiParam);
            if (tempPredicate != null)
            {
                predicate = predicate.And(tempPredicate);
            }

            beginTime = null; 
            endTime = null;
            GetBeginEndTime(apiParam, ref beginTime, ref endTime);
            contextCodes = apiParam != null && apiParam.ContextCodes != null && apiParam.ContextCodes.Any() ? apiParam.ContextCodes.ToArray() : null;
        }

        private void GetBeginTimeEndTimeConntext(ApiParam apiParam, out DateTime? beginTime, out DateTime? endTime, out string[] contextCodes)
        {
            beginTime = null; 
            endTime = null;
            GetBeginEndTime(apiParam, ref beginTime, ref endTime);
            contextCodes = apiParam != null && apiParam.ContextCodes != null && apiParam.ContextCodes.Any() ? apiParam.ContextCodes.ToArray() : null;
        }

        private void DoCheckEntityVali<T>(T entity) where T : EntityBase, new()
        {
            if (entity == null)
            {
                return;
            }

            if (string.IsNullOrEmpty(entity.id))
            {
                throw new CommonException("entity id is null");
            }
        }

        private void CheckEntityVali<T>(List<T> entities) where T : EntityBase, new()
        {
            if (entities == null || !entities.Any())
            {
                return;
            }

            entities.ForEach(t =>
            {
                DoCheckEntityVali(t);
            });
        }

        private void SetEnitityInfo<T>(T entity, bool isAdd)
            where T : EntityBase, new()
        {
            if (isAdd)
            {
                entity.create_time = DateTime.Now;
                entity.modify_time = DateTime.Now;
            }
            else
            {
                entity.modify_time = DateTime.Now;
            }
        }

        private void SetEnitityInfo<T>(List<T> entities, bool isAdd) 
            where T : EntityBase, new()
        {
            if (entities == null || !entities.Any())
            {
                return;
            }

            foreach (var item in entities)
            {
                this.SetEnitityInfo(item, isAdd);
            }
        }

        private void AddRemoveDistributeCacheKeys(List<string> distributedCacheKeys)
        {
        }

        private void CheckUniqueEntities<T>(List<T> entities, List<T> entitiesDb, List<string> checkKeys) 
            where T : EntityBase, new()
        {
            string entityName = typeof(T).Name;
            var entityConfig = EntityConfigManager.GetEntityConfigByName(entityName);
            if (entityConfig == null || !entityConfig.Fields.Any() || !entities.Any())
            {
                return;
            }

            var checkFields = entityConfig.Fields.Where(t => t.IsUnique);
            if (!checkFields.Any())
            {
                return;
            }

            foreach (var fc in checkFields)
            {
                var pro = typeof(T).GetProperty(fc.Name);
                if (pro == null)
                {
                    continue;
                }

                var groupOrgEntities = entities.GroupBy(t => t.org_id).ToList();
                foreach (var g in groupOrgEntities)
                {
                    var entitiesGroup = g.ToList();
                    var orgId = g.Key;
                    ApiParam apiParam = new ApiParam();
                    ApiGroupParam groupP = new ApiGroupParam() { IsAnd = false };
                    apiParam.Group.Groups.Add(groupP);
                    int count = 0;
                    bool checkSelf = entitiesGroup.Count() != 1;
                    foreach (var e in entitiesGroup)
                    {
                        object obj = pro.GetValue(e, null);
                        var value = obj?.ToString();
                        var entityDb = entitiesDb?.FirstOrDefault(t => t.id == e.id);
                        object objDb = entityDb != null ? pro.GetValue(entityDb, null) : null;
                        var valueDb = objDb?.ToString();
                        if (string.IsNullOrEmpty(value))
                        {
                            continue;
                        }

                        if (!string.IsNullOrEmpty(valueDb) && !string.IsNullOrEmpty(value) && valueDb == value)
                        {
                            continue;
                        }

                        ApiGroupParam groupParam = new ApiGroupParam();
                        groupParam.Rules.Add(new ApiRuleParam() { Field = "id", Operate = ApiRuleOperateEnum.不等于, Value = e.id });
                        groupParam.Rules.Add(new ApiRuleParam() { Field = fc.Name, Operate = ApiRuleOperateEnum.等于, Value = value });
                        groupP.Groups.Add(groupParam);
                        count++;
                        if (count >= 100)
                        {
                            DoCheckUniqueEntities(apiParam, checkSelf, fc, orgId, entitiesGroup, entities);
                            apiParam = new ApiParam();
                            groupP = new ApiGroupParam() { IsAnd = false };
                            apiParam.Group.Groups.Add(groupP);
                        }
                    }

                    if (groupP.Groups.Any())
                    {
                        DoCheckUniqueEntities(apiParam, checkSelf, fc, orgId, entitiesGroup, entities);
                    }
                }
            }
        }

        private void DoCheckUniqueEntities<T>(ApiParam apiParam, bool checkSelf, EntityFieldConfig entityFieldConfig, string orgId, List<T> entitiesGroup, List<T> entities)
             where T : EntityBase, new()
        {
            Expression<Func<T, bool>> express = t => true;
            if (entityFieldConfig.IsUniqueByOrg)
            {
                express = express.And(t => t.org_id == orgId);
            }

            express = express.And(apiParam.Group.GetExpression<T>());
            T existRecord = null;
            if (checkSelf)
            {
                existRecord = entityFieldConfig.IsUniqueByOrg ? entitiesGroup.FirstOrDefault(express.Compile()) : entities.FirstOrDefault(express.Compile());
            }

            if (existRecord == null)
            {
                DateTime b = DateTime.Now.Date.AddDays(-30);
                DateTime e = DateTime.Now.Date.AddDays(1).AddSeconds(-1);
                Dictionary<string, int> orders = new Dictionary<string, int>
                {
                    { "id", 0 }
                };
                existRecord = this.ContextPool.GetEntity<T>(b, e, express, orders, null, null);
            }

            if (existRecord != null)
            {
                var type = typeof(T);
                var p = type.GetProperty(entityFieldConfig.Name);
                string pv = string.Empty, ov = string.Empty;
                if (p != null)
                {
                    pv = LibSysUtils.ToString(p.GetValue(existRecord, null));
                }

                CommonEFUtils.ThrowFieldUniqueError(Provider, entityFieldConfig.Name, typeof(T).Name, entityFieldConfig.Name, pv, ov);
            }
        }

        private void DoSaveEntity<T>(List<T> entities, params string[] updateProperies)
            where T : EntityBase, new()
        {
            if (entities == null || !entities.Any())
            {
                return;
            }

            List<string> distributedCacheKeys = new List<string>();
            try
            {
                CheckEntityVali(entities);
                var deleteEntities = entities.Where(t => t.is_deleted).ToList();
                var saveEntities = entities.Where(t => !t.is_deleted).ToList();
                var entityIds = saveEntities.Select(t => t.id).ToList();
                var entitiesDb = this.ContextPool.GetEntities<T>(entityIds, null);
                var endtityDbIds = entitiesDb.Select(t => t.id).ToList();
                var updateEntities = saveEntities.Where(t => endtityDbIds.Contains(t.id)).ToList();
                var addEntities = saveEntities.Where(t => !endtityDbIds.Contains(t.id)).ToList();
                if (updateEntities.Any() || addEntities.Any())
                {
                    List<T> checkUnitqueEntities = new List<T>();
                    checkUnitqueEntities.AddRange(updateEntities);
                    checkUnitqueEntities.AddRange(addEntities);
                    CheckUniqueEntities<T>(checkUnitqueEntities, entitiesDb, distributedCacheKeys);
                }

                if (updateEntities.Any())
                {
                    this.SetEnitityInfo(updateEntities, false);
                    this.ContextPool.UpdateEntities<T>(updateEntities, entitiesDb, updateProperies);
                }

                if (addEntities.Any())
                {
                    this.SetEnitityInfo(addEntities, true);
                    this.ContextPool.AddEntities<T>(addEntities);
                }

                if (deleteEntities.Any())
                {
                    var deleteEntityIds = deleteEntities.Select(t => t.id).ToList();
                    this.ContextPool.DeleteEntity<T>(deleteEntityIds);
                }
            }
            finally
            {
                AddRemoveDistributeCacheKeys(distributedCacheKeys);
            }
        }
    }
}
